package com.tqz.rc.api;

import com.tqz.rc.common.util.CollectionUtils;

import java.util.*;

/**
 * <p>集群的实体对象。
 *
 * @author tianqingzhao
 * @since 2021/8/26 16:28
 */
public class Cluster {

    /**
     * 该集群所在的服务名称。
     */
    private String serviceName;

    /**
     * 该集群的名称。
     */
    private String clusterName;

    /**
     * 临时实例的集合。
     */
    private Set<Instance> ephemeralInstances = new HashSet<>();

    private Set<Instance> persistentInstances = new HashSet<>();

    public Cluster() {

    }

    public Cluster(String clusterName) {
        this.clusterName = clusterName;
    }

    public String getServiceName() {
        return serviceName;
    }

    public void setServiceName(String serviceName) {
        this.serviceName = serviceName;
    }

    public String getClusterName() {
        return clusterName;
    }

    public void setClusterName(String clusterName) {
        this.clusterName = clusterName;
    }

    public Set<Instance> getEphemeralInstances() {
        return ephemeralInstances;
    }

    public void setEphemeralInstances(Set<Instance> ephemeralInstances) {
        this.ephemeralInstances = ephemeralInstances;
    }

    public Set<Instance> getPersistentInstances() {
        return persistentInstances;
    }

    public void setPersistentInstances(Set<Instance> persistentInstances) {
        this.persistentInstances = persistentInstances;
    }

    /**
     * 初始化集群
     */
    public void init() {

    }

    /**
     * 真正的更改该集群下面所有的实例集合，也就是把该方法的最新实例集合参数ips赋值到临时节点或者持久节点上面。
     *
     * @param ips       最新的实例集合。
     * @param ephemeral 是否是临时节点，<code>true</code>为是，<code>false</code>为不是。
     */
    public void updateIps(List<Instance> ips, boolean ephemeral) {

        // 为了防止读写并发冲突，把之前的实例集合赋值到新的变量上，最后把变更的实例赋值到全局变量上面，CopyOnWrite的思想；
        // eureka防止并发冲突用的是二级缓存，只读缓存、读写缓存。
        Set<Instance> toUpdateInstances = ephemeral ? ephemeralInstances : persistentInstances;

        Map<String, Instance> oldIpMap = new HashMap<>(toUpdateInstances.size());

        for (Instance ip : toUpdateInstances) {
            oldIpMap.put(ip.getDatumKey(), ip);
        }

        // 获取修改的节点，设置其健康状态。这里修改的是该集群之前的节点。
        List<Instance> updatedIps = updateIps(ips, oldIpMap.values());
        if (updatedIps.size() > 0) {
            for (Instance ip : updatedIps) {
                Instance oldIP = oldIpMap.get(ip.getDatumKey());

                // 该实例没有被标记时，就更新该实例的最新健康状态
                if (!ip.isMarked()) {
                    ip.setHealthy(oldIP.isHealthy());
                }
            }
        }

        // TODO Nacos还有重置新节点的健康状态

        // TODO Nacos还有从该集群之前的集合里面移除掉不存在的节点的健康检查状态。
        //  也就是把最新的实例集合与之前的实例集合相比，如果老的实例节点不存在新的实例集合里面，就把老节点的健康检查状态移除掉。

        toUpdateInstances = new HashSet<>(ips);

        if (ephemeral) {
            ephemeralInstances = toUpdateInstances;
        } else {
            persistentInstances = toUpdateInstances;
        }
    }

    /**
     * 真正的更改集群下面的实例。
     *
     * @param newInstance 新的实例集合
     * @param oldInstance 老的实例集合
     * @return
     */
    private List<Instance> updateIps(Collection<Instance> newInstance, Collection<Instance> oldInstance) {
        List<Instance> intersection = (List<Instance>) CollectionUtils.intersection(newInstance, oldInstance);

        Map<String, Instance> stringIpAddressMap = new HashMap<>(intersection.size());

        for (Instance instance : intersection) {
            stringIpAddressMap.put(instance.getIp() + ":" + instance.getPort(), instance);
        }

        Map<String, Integer> intersectionMap = new HashMap<>(newInstance.size() + oldInstance.size());
        Map<String, Instance> updateIntersectionMap = new HashMap<>(newInstance.size());
        Map<String, Instance> newIntersectionMap = new HashMap<>(newInstance.size());

        for (Instance instance : oldInstance) {
            if (stringIpAddressMap.containsKey(instance.getIp() + ":" + instance.getPort())) {
                intersectionMap.put(instance.toString(), 1);
            }
        }

        for (Instance instance : newInstance) {
            if (stringIpAddressMap.containsKey(instance.getIp() + ":" + instance.getPort())) {
                if (intersectionMap.containsKey(instance)) {
                    intersectionMap.put(instance.toString(), 2);
                } else {
                    intersectionMap.put(instance.toString(), 1);
                }
            }

            newIntersectionMap.put(instance.toString(), instance);
        }

        for (Map.Entry<String, Integer> entry : intersectionMap.entrySet()) {
            String key = entry.getKey();
            Integer value = entry.getValue();

            if (value == 1) {
                if (newInstance.contains(key)) {
                    updateIntersectionMap.put(key, newIntersectionMap.get(key));
                }
            }
        }

        return new ArrayList<>(updateIntersectionMap.values());
    }

    /**
     * 获取该集群所有的实例
     *
     * @param ephemeral 是否是临时节点，<code>true</code>为是，<code>false</code>为不是
     * @return
     */
    public List<Instance> allIps(boolean ephemeral) {
        return ephemeral ? new ArrayList<>(ephemeralInstances) : new ArrayList<>(persistentInstances);
    }
}
